package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.common.constants.WemediaConstants;
import com.heima.common.exception.CustomException;
import com.heima.feign.article.IArticleClient;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmSensitive;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmSensitiveMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.GreenScanService;
import com.heima.wemedia.service.WmNewsAutoScanService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
@Transactional
@SuppressWarnings("all")//解决feign远程调用，接口有多个实现类的，注入的时候爆红线
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {
    @Autowired
    private WmNewsMapper wmNewsMapper;
    @Autowired
    private GreenScanService greenScanService;
    @Autowired
    private WmChannelMapper wmChannelMapper;
    @Autowired
    private WmUserMapper wmUserMapper;
    @Autowired
    private IArticleClient iArticleClient;
    @Autowired
    private WmSensitiveMapper wmSensitiveMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 自媒体文章审核
     *
     * @param id
     */
    //@Async //表明当前方法是一个异步方法 循环依赖 不能使用异步 // todo 三级缓存0.o
    public void autoScanWmNews(Integer id) {
        //代码健壮性 todo 分布式锁
        WmNews wmNews = null;
        int count = 1;
        while (count <= 3) {
            wmNews = wmNewsMapper.selectById(id);
            if (wmNews == null) {
                try {
                    log.error("第" + count + "次获取失败");
                    Thread.sleep(100);
                    count++;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else break;
        }
        //1、根据文章id查询数据库文章信息
        if (wmNews == null || StringUtils.isEmpty(wmNews.getContent())) {
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //2、提取文章中的文字和图片(发布的文章)
        if (wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())) {
            Map<String, Object> map = handleTextAndImage(wmNews);
            //2.1、敏感词过滤 基于dfa算法实现，本质上就是map集合
            boolean isSensitiveScan = handleSensiticeScan((String) map.get("text"), wmNews);
            if (!isSensitiveScan) return;
            //3、审核文字
            boolean isTextScan = scanText((String) map.get("text"), wmNews);
            if (!isTextScan) return;
            //4、审核图片
            boolean isImageScan = scanImage((List<String>) map.get("image"), wmNews);
            if (!isImageScan) return;
            //5、远程调用article微服务，进行app文章保存
            ResponseResult responseResult = saveAppArticle(wmNews);
            //6、判断是否成功
            if (!responseResult.getCode().equals(200)) {
                //响应码不是200 则失败
                throw new RuntimeException("WmNewsAutoScanServiceImpl - 文章审核，保存app端相关文章数据失败");
            }
            //回填article_id
            wmNews.setArticleId(Long.valueOf(responseResult.getData().toString()));
            updateNews(wmNews, WmNews.Status.PUBLISHED.getCode(), "审核成功");
        }

    }

    /**
     * 敏感词过滤
     *
     * @param text
     * @param wmNews
     * @return
     */
    public boolean handleSensiticeScan(String text, WmNews wmNews) {
        //信号位
        boolean flag = true;
        //1、从redis里面读
        List<String> sensitiveList = (List<String>) redisTemplate.opsForValue().get("Sensitive");
        if (sensitiveList == null) {
            //没有再从数据库中读取
            List<WmSensitive> wmSensitives = wmSensitiveMapper.selectList(Wrappers.<WmSensitive>lambdaQuery().select(WmSensitive::getSensitives));
            //加工只要里面的sensitive
            sensitiveList = wmSensitives.stream().map(WmSensitive::getSensitives).collect(Collectors.toList());
            //存入redis
            redisTemplate.opsForValue().set("Sensitive", sensitiveList);
        }
        //初始化敏感词库
        SensitiveWordUtil.initMap(sensitiveList);
        //敏感词查询
        Map<String, Integer> map = SensitiveWordUtil.matchWords(text);
        if (map.size() > 0 && wmNews != null) {
            //平常情况
            updateNews(wmNews, WmNews.Status.FAIL.getCode(), "此文章内容包含敏感词" + map);
            //信号位
            flag = false;
        } else if (map.size() > 0 && wmNews == null) {
            //orc读取图片文字进行审核，后续还会进行数据库的修改，所以这里不用改
            flag = false;
        }
        return flag;
    }

    /**
     * 保存app端相关的文章数据
     *
     * @param wmNews
     */
    public ResponseResult saveAppArticle(WmNews wmNews) {
        //1、创建一个远程调用所需要的dto
        ArticleDto articleDto = new ArticleDto();
        //2、属性拷贝（大部分）
        BeanUtils.copyProperties(wmNews, articleDto);
        //3、不能拷贝的属性
        //主键id
        if (wmNews.getArticleId() != null) {
            //说明不是新增，是修改，则此时需要给dto携带文章id
            articleDto.setId(wmNews.getArticleId());
        }
        //频道名称
        WmChannel wmChannel = wmChannelMapper.selectById(wmNews.getChannelId());
        articleDto.setChannelName(wmChannel.getName());
        //作者id
        articleDto.setAuthorId(wmNews.getUserId().longValue());
        //作者名称
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        if (wmUser != null) {
            articleDto.setAuthorName(wmUser.getName());
        }
        //文章布局
        articleDto.setLayout(wmNews.getType());

        //4、调用远程接口
        return iArticleClient.savaOrUpdate(articleDto);
    }

    /**
     * 审核图片
     *
     * @param images
     * @param wmNews
     * @return
     */
    private boolean scanImage(List<String> images, WmNews wmNews) {
        //信号位
        boolean flag = true;
        // 1、若无图 不许审核，直接返回
        if (images == null || images.size() == 0) {
            return flag;
        }
        // 图片去重
        images = images.stream().distinct().collect(Collectors.toList());
        //2、遍历审核 百度云只能一个一个审核
        for (String image : images) {
            try {
                // 3、调用百度云接口审核
                Map map = greenScanService.scanImage(image);
                // 代码健壮性
                if (map != null) {
                    if ((int) map.get("conclusionType") == WemediaConstants.BAIDU_SCAN_ILLEGAL) {
                        //含有违规图片
                        updateNews(wmNews, WmNews.Status.FAIL.getCode(), "此文章图片内容违规");
                        //信号位
                        flag = false;
                        //跳出循环
                        break;
                    }
                    if ((int) map.get("conclusionType") == WemediaConstants.BAIDU_SCAN_SUSPECTED) {
                        //疑似违规
                        updateNews(wmNews, WmNews.Status.ADMIN_AUTH.getCode(), "此文章内容图片疑似违规，需人工审核");
                        //信号位
                        flag = false;
                        //跳出循环
                        break;
                    }
                }
            } catch (Exception e) {
                flag = false; //有异常也说明审核失败
                e.printStackTrace();
            }
        }
        return flag;
    }

    /**
     * 提取文章中的文字和图片
     *
     * @param wmNews
     * @return
     */
    private Map<String, Object> handleTextAndImage(WmNews wmNews) {
        //1、创建容器储存纯文本内容  先将文章标题 以及标签加入其中 以待审核
        StringBuilder stringBuilder = new StringBuilder(wmNews.getTitle());
        stringBuilder.append(wmNews.getLabels());
        //2、创建容器储存所有图片相关信息
        List<String> imageList = new ArrayList<>();
        //3、创建map集合封装结果数据 返回
        Map<String, Object> map = new HashMap<>();
        //4、解析自媒体文章内容中的图片以及文本 有效则继续走
        if (StringUtils.isNotEmpty(wmNews.getContent())) {
            JSONArray jsonArray = JSON.parseArray(wmNews.getContent());
            //遍历
            for (Object json : jsonArray) {
                JSONObject jsonObject = JSON.parseObject(json.toString());
                // 文本
                if (jsonObject.get("type").equals("text")) {
                    //添加到容器
                    stringBuilder.append(jsonObject.get("value").toString());
                }
                //图片
                if (jsonObject.get("type").equals("image")) {
                    //添加到容器
                    imageList.add(jsonObject.get("value").toString());
                }
            }
        }
        //5、处理封面图片
        if (wmNews.getImages() != null) {
            //先讲图片以 ， 分割
            String[] split = wmNews.getImages().split(",");
            //添加到容器
            imageList.addAll(Arrays.asList(split)); //有点儿智能
        }
        //6、封装数据 返回
        map.put("text", stringBuilder.toString());
        map.put("image", imageList);
        return map;
    }

    /**
     * 审核文本
     *
     * @param text
     * @param wmNews
     * @return
     */
    private boolean scanText(String text, WmNews wmNews) {
        // 信号位
        boolean flag = true;
        if (StringUtils.isEmpty(text)) {
            return flag;
        }
        try {
            //调用百度API进行内容审核
            Map map = greenScanService.scanText(text);
            //健壮性判断
            if (map != null) {
                if ((int) map.get("conclusionType") == WemediaConstants.BAIDU_SCAN_ILLEGAL) {
                    //不合规，修改数据库数据
                    updateNews(wmNews, WmNews.Status.FAIL.getCode(), "此文章含有违规内容");
                    //修改信号位
                    flag = false;
                }
                if ((int) map.get("conclusionType") == WemediaConstants.BAIDU_SCAN_SUSPECTED) {
                    //疑似违规，需人工审核
                    updateNews(wmNews, WmNews.Status.ADMIN_AUTH.getCode(), "此文章中疑似有违规内容，需要人工审核");
                }
            }
        } catch (Exception e) {
            flag = false;
            e.printStackTrace();
        }
        //返回信号位
        return flag;
    }

    /**
     * 修改文章内容
     *
     * @param wmNews
     * @param type
     * @param reason
     */
    private void updateNews(WmNews wmNews, Short type, String reason) {
        wmNews.setStatus(type);
        wmNews.setReason(reason);
        wmNewsMapper.updateById(wmNews);
    }
}
